<template>
  <div class="container">
    <div class="header">
      <input v-model="searchNode1" type="text" placeholder="Search for a node..." @focus="setActiveSearch(0)" @input="highlightNode(0)" />
      <span v-if="getInfo(searchNode1)" class="info-span">
        <span class="type-indicator" :class="getTypeIndicator(searchNode1)">{{ getTypeIndicator(searchNode1) }}</span>
        {{ getInfo(searchNode1) }}
      </span>
      <input v-model="searchNode2" type="text" placeholder="Search for a node..." @focus="setActiveSearch(1)" @input="highlightNode(1)" />
      <span v-if="getInfo(searchNode2)" class="info-span">
        <span class="type-indicator" :class="getTypeIndicator(searchNode2)">{{ getTypeIndicator(searchNode2) }}</span>
        {{ getInfo(searchNode2) }}
      </span>
    </div>
    <svg ref="scatterSvg" width="1200" height="400"></svg>
    <div id="tooltip" style="display: none; position: absolute; background: white; border: 1px solid #ccc; padding: 5px;"></div>
  </div>
</template>

<script setup>
import { onMounted, ref, watch, computed } from 'vue';
import * as d3 from 'd3';
import { useStore } from 'vuex';
import { mapState } from 'vuex';
import { eventBus } from '../eventBus';

const store = useStore();
const scatterSvg = ref(null);
const data = ref([]);
const searchNode1 = ref('');
const searchNode2 = ref('');
const highlightedNodes = ref([null, null]);
const activeSearch = ref(0); // 当前激活的搜索框索引
const movieInfo = ref({});
const roleInfo = ref({});

// 读取电影信息
const fetchMovieInfo = async () => {
  const response = await fetch('src/assets/movie_info.txt');
  const text = await response.text();
  const lines = text.split('\n');
  lines.forEach(line => {
    const [id, name, _] = line.split('\t');
    movieInfo.value[id] = name;
  });
};

// 读取角色信息
const fetchRoleInfo = async () => {
  const response = await fetch('src/assets/role_info.txt');
  const text = await response.text();
  const lines = text.split('\n');
  lines.forEach(line => {
    const [id, name] = line.split('\t');
    roleInfo.value[id] = name;
  });
};

const getInfo = (nodeId) => {
  if (!nodeId) return '';
  const idWithoutPrefix = nodeId.replace('m', '').replace('p', '');
  if (nodeId.startsWith('m')) {
    return movieInfo.value[idWithoutPrefix] || '';
  } else if (nodeId.startsWith('p')) {
    return roleInfo.value[idWithoutPrefix] || '';
  }
  return '';
};

const getTypeIndicator = (nodeId) => {
  return nodeId.startsWith('m') ? 'movie：' : 'person：';
};

// 发送两个节点的 ID 到事件总线
const emitNodeIds = () => {
  eventBus.emit('node-ids', { id1: searchNode1.value.trim(), id2: searchNode2.value.trim() });
};

// 监听搜索框变化事件
watch([searchNode1, searchNode2], () => {
  emitNodeIds();
}, { immediate: true });

// 映射状态（如果需要）
const { SELECTDATASET } = mapState(['SELECTDATASET']);
const datasetName = computed(() => store.state.SELECTDATASET);

// 异步加载 umap.json 文件
const fetchData = async () => {
  try {
    const response = await fetch(`src/assets/umap_results_${datasetName.value}.json`);
    const jsonData = await response.json();
    data.value = jsonData.map(node => ({
      ...node,
      ...getNodeOffset(node.node),
      x: node.x || 0, // 确保 x 和 y 存在
      y: node.y || 0
    }));
  } catch (error) {
    console.error('Error fetching data:', error);
  }
};

let xScale, yScale, margin, width, height, zoomTransform;

const drawScatterPlot = () => {
  const svg = d3.select(scatterSvg.value);

  margin = { top: 20, right: 20, bottom: 30, left: 40 };
  width = 1200 - margin.left - margin.right;
  height = 400 - margin.top - margin.bottom;

  xScale = d3.scaleLinear()
    .domain([d3.min(data.value, d => d.x), d3.max(data.value, d => d.x)])
    .range([margin.left, width + margin.left]);

  yScale = d3.scaleLinear()
    .domain([d3.min(data.value, d => d.y), d3.max(data.value, d => d.y)])
    .range([height + margin.top, margin.top]);

  // 清空 SVG
  svg.selectAll('*').remove();

  svg.selectAll('circle')
    .data(data.value)
    .enter()
    .append('circle')
    .attr('cx', d => xScale(d.x) + d.offsetX)
    .attr('cy', d => yScale(d.y) + d.offsetY)
    .attr('r', 3)
    .attr('fill', d => {
      if (d.node.startsWith('m')) {
        return 'plum'; // 浅紫色
      } else if (d.node.startsWith('p')) {
        return 'powderblue'; // 浅青色
      } else {
        return d.node.trim() in highlightedNodes.value ? 'red' : 'black';
      }
    });

  // 添加高亮显示
  svg.selectAll('circle')
    .filter(d => highlightedNodes.value.includes(d.node.trim()))
    .attr('r', 6)
    .attr('stroke', 'red')
    .attr('stroke-width', 2);
};

const enableZoom = () => {
  const svg = d3.select(scatterSvg.value);
  const zoomBehavior = d3.zoom()
    .scaleExtent([0.5, 200]) // 缩放范围
    .on("zoom", handleZoom);

  svg.call(zoomBehavior);

  function handleZoom(event) {
    zoomTransform = event.transform;
    const newXScale = zoomTransform.rescaleX(xScale);
    const newYScale = zoomTransform.rescaleY(yScale);

    svg.selectAll('circle')
      .attr('cx', d => newXScale(d.x) + d.offsetX)
      .attr('cy', d => newYScale(d.y) + d.offsetY);
  }
};

onMounted(async () => {
  await fetchMovieInfo();
  await fetchRoleInfo();
  fetchData().then(() => {
    disperseNodes();
    drawScatterPlot();
    addMouseListeners();
    enableZoom(); // 启用缩放
  });
});

watch(datasetName, (newValue, oldValue) => {
  fetchData().then(() => {
    disperseNodes();
    drawScatterPlot();
    addMouseListeners();
    enableZoom(); // 重新启用缩放
  });
}, { immediate: true });

// 计算基于节点名称的偏移量
const getNodeOffset = (nodeName) => {
  const hash = nodeName.split('').reduce((acc, char) => acc + char.charCodeAt(0), 0);
  return {
    offsetX: (hash % 30) - 10, // 生成 -10 到 10 之间的随机数
    offsetY: (hash % 23) - 10  // 生成 -10 到 10 之间的随机数
  };
};

// 使用四叉树来加速邻近节点的查找
const disperseNodes = () => {
  const minDistance = 10; // 最小允许的距离
  const quadtree = d3.quadtree()
    .x(d => d.x + d.offsetX)
    .y(d => d.y + d.offsetY)
    .addAll(data.value);

  data.value.forEach(node => {
    const neighbors = quadtree.visit(neighbors => {
      if (neighbors.data && neighbors.data !== node) {
        const dx = node.x + node.offsetX - (neighbors.data.x + neighbors.data.offsetX);
        const dy = node.y + node.offsetY - (neighbors.data.y + neighbors.data.offsetY);
        const distance = Math.sqrt(dx * dx + dy * dy);

        if (distance < minDistance) {
          const angle = Math.atan2(dy, dx);
          const offset = (minDistance - distance) / 2;

          node.offsetX += Math.cos(angle) * offset;
          node.offsetY += Math.sin(angle) * offset;
          neighbors.data.offsetX -= Math.cos(angle) * offset;
          neighbors.data.offsetY -= Math.sin(angle) * offset;

          return false; // 继续遍历
        }
      }
      return true; // 停止遍历
    });
  });
};

// 添加鼠标事件监听器
const addMouseListeners = () => {
  const svg = d3.select(scatterSvg.value);
  const tooltip = document.getElementById('tooltip');

  svg.on('mousemove', (event) => {
    const [mouseX, mouseY] = d3.pointer(event, scatterSvg.value);
    const foundPoint = findNearestPoint(mouseX, mouseY);

    if (foundPoint) {
      tooltip.style.display = 'block';
      tooltip.style.left = `${event.pageX + 10}px`;
      tooltip.style.top = `${event.pageY + 10}px`;

      // tooltip.innerHTML = `<pre>${JSON.stringify(foundPoint, null, 2)}</pre>`;
      tooltip.innerHTML = `<pre>${foundPoint.node}</pre>`;
    } else {
      tooltip.style.display = 'none';
    }
  });

  svg.on('click', (event) => {
    const [mouseX, mouseY] = d3.pointer(event, scatterSvg.value);
    const foundPoint = findNearestPoint(mouseX, mouseY);

    if (foundPoint) {
      if (activeSearch.value === 0) {
        searchNode1.value = foundPoint.node.trim();
      } else if (activeSearch.value === 1) {
        searchNode2.value = foundPoint.node.trim();
      }
      highlightNode(activeSearch.value); // 高亮显示节点
    }
  });
};

// 查找最近的点
const findNearestPoint = (mouseX, mouseY) => {
  const newXScale = zoomTransform ? zoomTransform.rescaleX(xScale) : xScale;
  const newYScale = zoomTransform ? zoomTransform.rescaleY(yScale) : yScale;

  return data.value.find(d => Math.hypot(newXScale(d.x) + d.offsetX - mouseX, newYScale(d.y) + d.offsetY - mouseY) <= 3);
};

// 设置当前激活的搜索框
const setActiveSearch = (index) => {
  activeSearch.value = index;
};

// 高亮显示节点
const highlightNode = (index) => {
  highlightedNodes.value[index] = [searchNode1.value, searchNode2.value][index].trim();
  drawScatterPlot(); // 重新绘制散点图以更新高亮显示
};
</script>

<style>
/* 设置 SVG 的边框 */
svg {
  border: 1px solid black;
  height: 40vh;
  width: 96%;
  margin-left: 1%;
}

#tooltip pre {
  margin: 0; /* 去除预格式化文本默认的边距 */
}

/* 容器样式 */
.container {
  display: flex;
  flex-direction: column;
  align-items: center;
}

.header {
  display: flex;
  align-items: center;
  width: 100%;
  margin-top: 1%;
  margin-bottom: 1%; /* 移除头部与画布间的间隔 */
}

.header h1 {
  margin: 0; /* 移除默认的 margin */
}

.header input {
  margin-left: 1%; /* 在标题和输入框之间添加一些间距 */
}

.info-span {
  margin-left: 5px;
  font-size: 14px;
  color: #000000;
}

.type-indicator {
  margin-right: 3px;
  font-size: 12px;
  color: #888;
  font-style: italic;
}

/* 添加样式以区分电影和人物 */
.type-indicator.movie： { color: plum; }
.type-indicator.person： { color: powderblue; }
</style>